Skip to content

fix URLSession crashing on HTTP/0.9 simple-responses #1097

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 9, 2017
Merged

fix URLSession crashing on HTTP/0.9 simple-responses #1097

merged 1 commit into from
Jul 9, 2017

Conversation

weissi
Copy link
Contributor

@weissi weissi commented Jul 6, 2017

also add tests for misbehaving HTTP servers.

we discovered that if the server is just returning random bytes, the whole app would crash with fatalError("Received body data, but the header is not complete, yet.") . The reason we can get into that state is that CURL support HTTP/0.9 simple-responses which are just the body bytes...

The issue is also discussed on CURL's github.

To make sure we don't regress and to document the current behaviour, I also wrote tests for a few other cases when the HTTP server is misbehaving or behaving oddly.

@weissi weissi requested review from phausler and pushkarnk July 6, 2017 10:47
@weissi
Copy link
Contributor Author

weissi commented Jul 6, 2017

@swift-ci please test

@@ -41,6 +41,10 @@ class TestURLSession : XCTestCase {
("test_customProtocolResponseWithDelegate", test_customProtocolResponseWithDelegate),
("test_httpRedirection", test_httpRedirection),
("test_httpRedirectionTimeout", test_httpRedirectionTimeout),
("test_illegalHTTPServerResponses", test_illegalHTTPServerResponses),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we're keeping the order of the tests the same as in the test list, this line should be moved to the end.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks @alblue , addressed

@weissi
Copy link
Contributor Author

weissi commented Jul 6, 2017

@swift-ci please test

1 similar comment
@weissi
Copy link
Contributor Author

weissi commented Jul 6, 2017

@swift-ci please test

@pushkarnk
Copy link
Member

This is interesting. The change and the tests look good to me.

Does Foundation on Darwin also gracefully handle such responses?

@weissi
Copy link
Contributor Author

weissi commented Jul 7, 2017

@pushkarnk two intentions in this pull request

  1. most importantly don't just crash the program on unexpected server response as that's terrible for the user
  2. document the current behaviour in unit tests

I haven't tried how regular Foundation behaves, but it certainly won't just crash ;). Do we want to match regular Foundation exactly or what is the intention here? With my patch we handle these not so common server responses just the way CURL handles them which is what we seem to do usually in Swift Foundation's URLSession.

@weissi
Copy link
Contributor Author

weissi commented Jul 7, 2017

@pushkarnk @parkera @phausler My suggestion would be to get this patch in as soon as possible to be sure it'll land in Swift 4 as it's just terrible if the program crashes if the server misbehaves. Worst is that if you connect to HTTP2-only servers you might hit that behaviour (as a HTTP2 only response looks like a HTTP/0.9 simple-response to CURL versions without HTTP2 support).

After we addressed the crasher issues we should talk about what we want to achieve here and how we want to handle all the special cases. See here for the list of odd server behaviours I could quickly come up with. We should then go through that list one by one and decide what the appropriate behaviour should be.

But IMHO more importantly we should get something in to fix the crashes. We can then always decide later that we want to throw away HTTP/0.9 simple responses even though CURL handles them.

@pushkarnk
Copy link
Member

Specifically, if the server is using HTTP/1.1 but the header is malformed (as you've demonstrated in the tests) does Darwin also return an HTTPURLResponse with httpVersion set to 0.9?

@pushkarnk
Copy link
Member

pushkarnk commented Jul 7, 2017

Do we want to match regular Foundation exactly or what is the intention here?

Yes, we'd ideally want to. Unless we are sure that the Darwin Foundation isn't doing the right thing

guard ts.isHeaderComplete else { fatalError("Received body data, but the header is not complete, yet.") }
guard case .transferInProgress(var ts) = internalState else { fatalError("Received body data, but no transfer in progress.") }
if !ts.isHeaderComplete {
ts.response = HTTPURLResponse(url: ts.url, statusCode: 200, httpVersion: "0.9", headerFields: [:])
Copy link
Member

@pushkarnk pushkarnk Jul 7, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this valid in the three different scenarios that you have pointed out?

  1. When the server sends a HTTP/0.9 response aka "only the body"
  2. When the server is running HTTP/1.1 but sends a malformed response
  3. When the server running HTTP/2 sends a response to client running HTTP/1.1
    ... I'm specifically concerned about the httpVersion: "0.9"

@pushkarnk
Copy link
Member

pushkarnk commented Jul 7, 2017

In short, I seem to have only two questions:

  1. Is the behaviour consistent with Darwin? (Do we desire that? I'd say yes!)
  2. Is the HTTPURLResponse being returned valid in all the failure scenarios pointed out?

Having said that, I agree that we need to fix these crashes first.
@parkera @phausler What would you suggest?

@weissi
Copy link
Contributor Author

weissi commented Jul 7, 2017

@pushkarnk so in Darwin Foundation, it's really a NSURLHTTPResponse that's created using a CFURLHTTPResponse that's created with a CFHTTPMessageCreateResponse and that is initialised with the following parameters:

* thread #2, queue = 'com.apple.CFNetwork.LoaderQ', stop reason = breakpoint 1.5
    frame #0: 0x00007fff2d1be705 CFNetwork`CFHTTPMessageCreateResponse
CFNetwork`CFHTTPMessageCreateResponse:
->  0x7fff2d1be705 <+0>: pushq  %rbp
    0x7fff2d1be706 <+1>: movq   %rsp, %rbp
    0x7fff2d1be709 <+4>: pushq  %r15
    0x7fff2d1be70b <+6>: pushq  %r14
Target 0: (URLSessionRepro) stopped.
(lldb) po $rdi
\<CFAllocator 0x7fff579b1f10 [0x7fff579b1f10]>{info = 0x7fff5def2000}

(lldb) po $rsi
200

(lldb) po $rdx
<nil>

(lldb) po $rcx
HTTP/0.9

(lldb) 

so it does indeed treat it as HTTP/0.9 too but the string seems to be HTTP/0.9 not just 0.9. I will change my patch accordingly.

also add tests for misbehaving HTTP servers.
@weissi
Copy link
Contributor Author

weissi commented Jul 7, 2017

@pushkarnk also the NSURLHTTPResponse doesn't actually seem to have a property so it seems like you won't be able to query the HTTP version anyway but I guess we want it compatible.

@weissi
Copy link
Contributor Author

weissi commented Jul 7, 2017

@swift-ci please test

1 similar comment
@pushkarnk
Copy link
Member

@swift-ci please test

@pushkarnk pushkarnk requested review from pushkarnk and removed request for pushkarnk July 7, 2017 09:52
@weissi
Copy link
Contributor Author

weissi commented Jul 8, 2017

@pushkarnk tests seemed to have passed. Any further comments/questions? @phausler happy with this?

@phausler
Copy link
Contributor

phausler commented Jul 9, 2017

The change looks sensible to me

@phausler phausler merged commit 79b511a into swiftlang:master Jul 9, 2017
kevints pushed a commit to kevints/swift-corelibs-foundation that referenced this pull request Aug 1, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants